/* McSocket.cpp
 *
 * --
 * Copyright (c) 2012, Christophe Huriaux
 * Distributed under the terms of the MIT license.
 * http://www.opensource.org/licenses/mit-license.php
 * --
 */

#include <socket/McSocket.h>
#include <debug/logger.h>
#include <sstream>

McSocket::McSocket(const char* mcServer, int serverPort)
	: Thread("PacketThread"), socket(mcServer, serverPort)
{
	pthread_mutex_init(&packetMutex, NULL);
}

void*
McSocket::run()
{
	bool packetComplete = false;
	int readCount = 1;
	unsigned char *buffer = new unsigned char[readCount];
	int field = 0;
	McPacketFromServer *packet;

	logg(debug) << "packetThread begin " << logg::endl;

	while (!shouldStop())
	{
		if (readCount == 0)
			packetComplete = true;
		else
		{
			int inBuffer = socket.count();
			int chunkSize = (readCount>inBuffer)?inBuffer:readCount;
			ssize_t rread = socket.read(buffer, chunkSize);
			readCount -= rread;

			if (readCount == 0)
				packetComplete = true;

			if (readCount < 0)
				logg(warning) << "readCount=" << readCount << logg::endl;
		}

		if (packetComplete)
		{
			if (field == 0)
			{
				if (packetInitialize(&packet, buffer[0]) != 0)
				{
					logg(critical) << "Unrecognized packet ID " << std::hex
						<< (int)((unsigned char*)buffer)[0] << std::dec << logg::endl;

					if (buffer != NULL)
						delete[] buffer;

					socket.stop();
					return NULL;
				}

				readCount = packet->setField(field++, NULL);
			}
			else
				readCount = packet->setField(field++, buffer);


			if (readCount == -1)
			{
				pthread_mutex_lock(&packetMutex);
				readCount = 1;
				field = 0;
				packetQueue.push(packet);
				logg(debug) << "Packet type " << std::hex << packet->getPacketId() << std::dec << " stored." << logg::endl;
				pthread_mutex_unlock(&packetMutex);
			}

			if (buffer != NULL)
				delete[] buffer;

			if (readCount > 0)
				buffer = new unsigned char[readCount];
			else
				buffer = NULL;

			packetComplete = false;
		}
		else
			usleep(10000);
	}

	logg(debug) << "packetThread end" << logg::endl;
	delete buffer;
	return NULL;
}

int
McSocket::open()
{
	if (socket.open() != 0)
		return -1;

	logg(debug) << logg::endl;
	return start();
}

int
McSocket::close()
{
	int r = 0;
	if (socket.close() != 0)
		r = -1;

	if (isAlive())
	{
		stop();
		if (join() != 0)
			r = -1;
	}

	return r;
}

McSocket&
McSocket::operator<<(McPacketToServer& packet)
{
	std::stringstream ss(std::stringstream::out | std::stringstream::binary);
	std::string pData;

	packet.forge(ss);
	pData = ss.str();

	socket.write(pData.c_str(), pData.length());

	return *this;
}

McSocket&
McSocket::operator>>(McPacketFromServer*& packet)
{
	pthread_mutex_lock(&packetMutex);

	packet = packetQueue.front();
	packetQueue.pop();

	pthread_mutex_unlock(&packetMutex);
	return *this;
}

unsigned int
McSocket::queue_size()
{
	unsigned int size;
	pthread_mutex_lock(&packetMutex);

	size = packetQueue.size();

	pthread_mutex_unlock(&packetMutex);

	return size;
}
